//
//  CalculatorBrain.swift
//  Calculator
//
//  Created by mqm on 16/6/3.
//  Copyright © 2016年 mqm. All rights reserved.
//

import Foundation

class CalculatorBrain
{
    //操作枚举
    private enum Op{
        case Operand(Double) //操作数
        case UnaryOperation(String,Double -> Double) //单操作数
        case BinaryOperation(String,(Double,Double) -> Double) //双操作数
    }
    
    private var opStack = [Op]();//初始化空数组，元素类型为枚举类型Op，同：var opStack = Array<Op>()
    
    //已知操作符对应的操作，String类型的为操作符，Op为对应操作
    private var knowOps = Dictionary<String,Op>()
    
    init(){
        knowOps["+"] = Op.BinaryOperation("+", +)
        knowOps["−"] = Op.BinaryOperation("−", {$1 - $0})
        knowOps["×"] = Op.BinaryOperation("×", *)
        knowOps["÷"] = Op.BinaryOperation("÷", {$1 / $0})
        knowOps["√"] = Op.UnaryOperation("√", sqrt)
     }
    
    //给opStack 添加操作数函数
    func pushOperand(operand:Double)->Double?{
        opStack.append(Op.Operand(operand))
        return evaluate()
    }
    
    //处理操作符
    func  performOperation(symbol:String) -> Double?{
        if let operation = knowOps[symbol] {
            opStack.append(operation)
        }
        return evaluate()
    }
    
    func evaluate()-> Double?{
        
        //调用 evaluate(ops:[Op])-> (result : Double?,remainingOps : [Op])
        let (result,_) = evaluate(opStack)
        return result
    }
    
    //递归处理opStack中的数据
    private func evaluate(ops:[Op])-> (result : Double?,remainingOps : [Op]){
        if !ops.isEmpty{
            var remainingOps = ops
            let  op = remainingOps.removeLast()
            switch op {
            case .Operand(let operand):
                return (operand,remainingOps)
            case .UnaryOperation(_, let operation):
                let operandEvaluation = evaluate(remainingOps)
                if let operand = operandEvaluation.result{
                    return (operation(operand),operandEvaluation.remainingOps)
                }
            case .BinaryOperation(_, let operation):
                let op1Evaluation = evaluate(remainingOps)
                if let operand1 = op1Evaluation.result {
                    let op2Evaluation = evaluate(op1Evaluation.remainingOps)
                    if let operand2 = op2Evaluation.result{
                        return (operation(operand1,operand2),op2Evaluation.remainingOps)
                    }
                }
            
            }
            
        }
        return (nil,ops)
    }
}